package scmp_algorithm;

import dipl.algorithm.impl.Parameters;
import dipl.algorithm.math.primitive.Point2d;
import dipl.algorithm.math.fp.primitive.Point2df;
import dipl.algorithm.math.utility.ApfloatUtils;
import dipl.algorithm.utility.convert.ApfloatConversion;
import java.util.Iterator;
import java.util.LinkedList;
import org.apfloat.Apfloat;

/**
 * Command-line algorithm starter
 * @author Marko Walther
 */
public class Main {

	/**
	 * Command indices
	 */
	public class Commands {
		/**
		 * statistic command
		 */
		public static final int COMMAND_PRINT_STATISTICS = 0;
		/**
		 * use supercomposition
		 */
		public static final int COMMAND_USE_SUPERCOMPOSITION = 1;
		/**
		 * isolate roots
		 */
		public static final int COMMAND_ROOT_ISOLATION = 2;
		/**
		 * use yap bounds
		 */
		public static final int COMMAND_USE_YAP = 3;
		/**
		 * used fixed precision arithmetic
		 */
		public static final int COMMAND_USE_FP = 4;
	}

	//
	// AUXILLIARY METHODS
	//

	/**
	 * dumps given collection of intersection to stderr
	 * @param inters
	 */
	protected static void DumpIntersectionsAP( Iterator<dipl.algorithm.impl.Intersection> inters ) {
		System.out.println();
		if( !inters.hasNext() ) {
			System.out.println( "No intersections." );
		}
		else {
			while( inters.hasNext() ) {
				System.out.println( inters.next().toString() );
			}
		}
	}

	/**
	 * dumps given collection of intersection to stdout
	 * @param inters
	 */
	protected static void DumpIntersectionsFP( Iterator<dipl.algorithm.impl.fp.Intersection> inters ) {
		System.out.println();
		if( !inters.hasNext() ) {
			System.out.println( "No intersections." );
		}
		else {
			while( inters.hasNext() ) {
				System.out.println( inters.next().toString() );
			}
		}
	}

	/**
	 * parses given string containing control point coordinates and returns
	 * a Bezier-curve.
	 * @param arg
	 * @return curve or <code>null</code> if parsing failed
	 */
	protected static Point2d[] ParseControlPolygonAP( String arg ) {
		Point2df[] temp = ParseControlPolygonFP( arg );
		if( temp == null )
			return null;
		Point2d[] controls = new Point2d[temp.length];
		for( int i = 0; i < controls.length; i++ ) {
			controls[i] = ApfloatConversion.ConvertPoint( temp[i] );
		}
		return controls;
	}

	/**
	 * parses given string containing control point coordinates and returns
	 * a Bezier-curve.
	 * @param arg
	 * @return curve or <code>null</code> if parsing failed
	 */
	protected static Point2df[] ParseControlPolygonFP( String arg ) {
		arg = arg.trim();
		if( !arg.startsWith( "{" ) || !arg.endsWith( "}" ) ) {
			System.err.println( "Wrong syntax in argument :"+arg+"!" );
			return null;
		}
		arg = arg.substring( 1, arg.length()-1 );
		// extract points
		String[] spoints = arg.split( ";" );
		Point2df[] controls = new Point2df[spoints.length];
		for( int i = 0; i < spoints.length; i++ ) {
			String t = spoints[i];
			t = t.trim();
			if( t.length() == 0 ) {
				System.err.println( "Wrong syntax in argument :"+arg+"!" );
				return null;
			}
			if( !t.startsWith( "(" ) || !t.endsWith( ")" ) ) {
				System.err.println( "Wrong syntax in argument :"+arg+"!" );
				return null;
			}
			t = t.substring( 1, t.length()-1 );
			t = t.trim();
			if( t.length() == 0 ) {
				System.err.println( "Wrong syntax in argument :"+arg+"!" );
				return null;
			}
			String[] coords = t.split( "," );
			if( coords.length != 2 ) {
				System.err.println( "Wrong syntax in argument :"+arg+"!" );
				return null;
			}
			controls[i] = new Point2df( Double.valueOf( coords[0] ), Double.valueOf( coords[1] ) );
		}
		if( controls.length == 0 ) {
			System.err.println( "No control points in argument :"+arg+"!" );
			return null;
		}
		return controls;
	}

	/**
	 * prints help for program usage
	 */
	public static void PrintHelp() {
		System.out.println( "Possible arguments:\n" );
		System.out.println( "-h ...................... Print help." );
		System.out.println( "-s ...................... Collect and print statistical data." );
		System.out.println( "-scmp ................... Use supercomposition-subdivision-method." );
		System.out.println( "-roots .................. Use root-isolation algorithm." );
		System.out.println( "-yap .................... Calculate Yap-bounds." );
		System.out.println( "-fp ..................... Use fixed-precision arithmetic (-yap is ignored)." );
		System.out.println( "-threshold=<value> ...... Sets emergency treshold" );
		System.out.println( "-L=<value> .............. Sets number of bits in input coefficients for Yap-bound calculation." );
		System.out.println( "{(x1,y1);...;(xn,yn)} ... Control polygon of input curve." );
		System.out.println( "\nExample:\n" );
		System.out.println( "Calculates intersection intervalls between bézier-curves with given control polygons using the" );
		System.out.println( "supercomposition-subdivision-method:" );
		System.out.println( "java -jar ./scmp_algorithm.jar -scmp {(1,2),(3,4),(0,2)} {(-1,-3),(1e5,0.02),(1.5e6,2e3)}" );
	}

	//
	// MAIN
	//

	/**
	 * @param args
	 */
	public static void main( String[] args ) {
		if( args.length == 0 ) {
			System.err.println( "Missing arguments. Use '-h' for help." );
			return;// 1;
		}
		boolean[] commands = new boolean[5];
		String argL = null;
		String argTreshold = null;

		LinkedList<String> args_poly = new LinkedList<String>();
		for( int i = 0; i < args.length; i++ ) {
			String arg = args[i];
			// command
			if( arg.compareToIgnoreCase( "-h" ) == 0 ) {
				PrintHelp();
				return;
			}
			else if( arg.compareToIgnoreCase( "-s" ) == 0 ) {
				commands[Commands.COMMAND_PRINT_STATISTICS] = true;
			}
			else if( arg.compareToIgnoreCase( "-scmp" ) == 0 ) {
				commands[Commands.COMMAND_USE_SUPERCOMPOSITION] = true;
			}
			else if( arg.compareToIgnoreCase( "-roots" ) == 0 ) {
				commands[Commands.COMMAND_ROOT_ISOLATION] = true;
			}
			else if( arg.compareToIgnoreCase( "-yap" ) == 0 ) {
				commands[Commands.COMMAND_USE_YAP] = true;
			}
			else if( arg.compareToIgnoreCase( "-fp" ) == 0 ) {
				commands[Commands.COMMAND_USE_FP] = true;
			}
			else if( arg.startsWith( "-threshold=" ) ) {
				argTreshold = arg.substring( 11 );
			}
			else if( arg.startsWith( "-L=" ) ) {
				argL = arg.substring( 3 );
			}
			// curve control polygons
			else if( arg.startsWith( "{" ) ) {
				args_poly.addLast( arg );
			}
			else {
				System.err.println( "Illegal argument: "+arg+"!" );
				return;// 1;
			}
		}

		// ------------------------------------------------------ get parameters

		int params = 0;
		if( commands[Commands.COMMAND_PRINT_STATISTICS] ) params |= Parameters.COLLECT_DATA;
		if( commands[Commands.COMMAND_USE_SUPERCOMPOSITION] ) params |= Parameters.SUPERCOMPOSITION;
		if( commands[Commands.COMMAND_USE_YAP] ) params |= Parameters.BOUNDS_YAP;

		double tresholdFP		= 1e-6;
		Apfloat tresholdAP	= ApfloatUtils.ValueOf( 1e-6 );
		int L = 0;
		if( argL != null ) L=Integer.parseInt( argL );

		// fixed precision or not?
		if( commands[Commands.COMMAND_USE_FP] ) {
			if( argTreshold != null ) tresholdFP = Double.valueOf( argTreshold );
		} else {
			if( argTreshold != null ) tresholdAP = ApfloatUtils.ValueOf( Double.valueOf( argTreshold ) );
		}

		// ======================================================================
		// ROOT ISOLATION
		// ======================================================================

		long startTime = 0, endTime = 0;
		try {
			if( commands[Commands.COMMAND_ROOT_ISOLATION] ) {
				if( args_poly.size() == 0 ) {
						System.err.println( "Input points missing!" );
						return;// 1;
				}
				if( commands[Commands.COMMAND_USE_FP] ) {
					Point2df[] pf = ParseControlPolygonFP( args_poly.getFirst() );
					if( pf == null )
						return;// 1;
					dipl.algorithm.math.fp.curve.BezierCurve f = new dipl.algorithm.math.fp.curve.BezierCurve( pf );
					dipl.algorithm.impl.fp.ScmpAlgorithmBezierRootIsolation a = new dipl.algorithm.impl.fp.ScmpAlgorithmBezierRootIsolation();
					startTime = System.currentTimeMillis();
					a.CalculateIntersections( f, params, tresholdFP );
					endTime = System.currentTimeMillis();
					DumpIntersectionsFP( a.Intersections() );
				}
				else {
					Point2d[] pf = ParseControlPolygonAP( args_poly.getFirst() );
					if( pf == null )
						return;// 1;
					dipl.algorithm.math.curve.BezierCurve f = new dipl.algorithm.math.curve.BezierCurve( pf );
					dipl.algorithm.impl.ScmpAlgorithmBezierRootIsolation a = new dipl.algorithm.impl.ScmpAlgorithmBezierRootIsolation();
					startTime = System.currentTimeMillis();
					a.CalculateIntersections( f, params, tresholdAP, L );
					endTime = System.currentTimeMillis();
					DumpIntersectionsAP( a.Intersections() );
					if( commands[Commands.COMMAND_PRINT_STATISTICS] ) {
						System.out.println( "Statistics:" );
						System.out.println( a.Statistics() );
					}
				}
			}
			// ======================================================================
			// NORMAL INTERSECTION
			// ======================================================================
			else {
				if( args_poly.size() < 2 ) {
					System.err.println( "Two input curves missing!" );
					return;// 1;
				}
				Point2d[] pf = ParseControlPolygonAP( args_poly.removeFirst() );
				if( pf == null )
					return;// 1;
				Point2d[] pg = ParseControlPolygonAP( args_poly.removeFirst() );
				if( pg == null )
					return;// 1;
				dipl.algorithm.math.curve.BezierCurve f = new dipl.algorithm.math.curve.BezierCurve( pf );
				dipl.algorithm.math.curve.BezierCurve g = new dipl.algorithm.math.curve.BezierCurve( pg );
				dipl.algorithm.impl.ScmpAlgorithmBezier a =
					new dipl.algorithm.impl.ScmpAlgorithmBezier();
				a.CalculateIntersections( f, g, params, tresholdAP, L );
				DumpIntersectionsAP( a.Intersections() );
				if( commands[Commands.COMMAND_PRINT_STATISTICS] ) {
					System.out.println( "Statistics:" );
					System.out.println( a.Statistics() );
				}
			}
		}
		catch( Exception e ) {
			e.printStackTrace( System.err );
			return;// 1;
		}
		System.out.println( "Needed "+((endTime-startTime)*0.001)+" seconds." );
		return;// 0;
	}
}
